#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <cliijpegd.h>

static int select_output(char *output_filename, cli_opt_t *opt)
{
	char *ext = get_filename_extension(output_filename);
	if (strcmp(ext, "yuv") == 0) {
		opt->i_csp = C_CSP_I420;
	} else if (strcmp(ext, "nv21") == 0) {
		opt->i_csp = C_CSP_NV21;
	} else if (strcmp(ext, "nv12") == 0) {
		opt->i_csp = C_CSP_NV12;
	} else if (strcmp(ext, "t420") == 0) {
		opt->i_csp = C_CSP_T420;
	} else {
		fprintf(stderr, "can't supported output file color space\n");
		goto err_unsupport_csp;
	}

	return 0;

err_unsupport_csp:
	return -1;
}

static int parse(int argc, char *argv[], ijpegd_param_t *param, cli_opt_t *opt)
{
	ijpegd_param_default(param);
	param->soc = atoi(argv[1]);
    if ((param->soc>3)||(param->soc<0))
    {
		fprintf(stderr, "soc failed\n");
        goto err_select_desoc;
    }
    param->i_max_width = atoi(argv[2]);
	param->i_max_height = atoi(argv[3]);
	param->b_tlb = atoi(argv[4]);
	opt->b_use_phy = (param->b_use_phy = atoi(argv[5]));
	if (param->b_use_phy) {
		opt->ckmalloc = (param->ckmalloc = c_kalloc);
		opt->ckfree = (param->ckfree = c_kfree);
		opt->ckvirt_to_phys = (param->ckvirt_to_phys = c_kvirt_to_kphys);
		opt->ckphys_to_virt = (param->ckphys_to_virt = c_kphys_to_kvirt);
	} else {
		opt->ckmalloc = (param->ckmalloc = c_malloc);
		opt->ckfree = (param->ckfree = c_free);
		opt->ckvirt_to_phys = (param->ckvirt_to_phys = c_virt_to_phys);
		opt->ckphys_to_virt = (param->ckphys_to_virt = c_phys_to_virt);
	}

	opt->input_file = fopen(argv[6], "rb");
	if (opt->input_file == NULL) {
		fprintf(stderr, "fopen %s failed\n", argv[6]);
		goto err_fopen_input_file;
	}

	if (fseek(opt->input_file, 0, SEEK_END) < 0) {
		fprintf(stderr, "fseek %s end failed\n", argv[6]);
		goto err_fseek_end_input_file;
	}

	opt->input_file_size = ftell(opt->input_file);
	if (opt->input_file_size < 0) {
		fprintf(stderr, "ftell %s end failed\n", argv[6]);
		goto err_ftell_end_input_file;
	}

	if (fseek(opt->input_file, 0, SEEK_SET) < 0) {
		fprintf(stderr, "fseek %s start failed\n", argv[6]);
		goto err_fseek_start_input_file;
	}

	if (select_output(argv[7], opt) < 0) {
		fprintf(stderr, "select_output failed\n");
		goto err_select_output;
	}

	param->i_csp = opt->i_csp;

	opt->output_file = fopen(argv[7], "wb+");
	if (opt->output_file == NULL) {
		fprintf(stderr, "fopen %s failed\n", argv[7]);
		goto err_fopen_output_file;
	}

	return 0;
err_select_desoc:
err_fopen_output_file:
err_select_output:
err_fseek_start_input_file:
err_ftell_end_input_file:
err_fseek_end_input_file:
	fclose(opt->input_file);
	opt->input_file = NULL;
err_fopen_input_file:
	return -1;
}

int init(int argc, char **argv, ijpegd_param_t *param, cli_opt_t *opt)
{
	if (argc < 8) {
		fprintf(stderr, "Usage: %s soc maxw maxh btlb busephy inputfile outputfile\n", argv[0]);
		fprintf(stderr, "        soc input number,4 :C_T21 3 :C_T30 2 :C_T20 1 :C_T10 0 :C_M200 \n");
		goto err_invalidate_cmdline;
	}

	if (parse(argc, argv, param, opt) < 0) {
		fprintf(stderr, "parse param failed\n");
		goto err_parse_param;
	}

	return 0;

err_parse_param:
err_invalidate_cmdline:
	return -1;
}

void deinit(ijpegd_param_t *param, cli_opt_t *opt)
{
	fclose(opt->input_file);
	fclose(opt->output_file);
	opt->input_file = NULL;
	opt->output_file = NULL;
}

int decode(ijpegd_param_t *param, cli_opt_t *opt, volatile const int *ctl)
{
	ijpegd_t *h = NULL;
	int piclen = c_align(param->i_max_width, C_VB_ALIGN, C_VB_ALIGN) * c_align(param->i_max_width, C_VB_ALIGN, C_VB_ALIGN);
	ijpegd_nal_t nal;
	ijpegd_pic_t pic_out;

	nal.i_payload = opt->input_file_size;
	nal.p_payload = calloc(opt->input_file_size, 1);
	if (nal.p_payload == NULL) {
		fprintf(stderr, "calloc nal.p_payload failed\n");
		goto err_calloc_nal_p_payload;
	}

	memset(&pic_out, 0, sizeof(ijpegd_pic_t));
	pic_out.virAddr = (intptr_t)calloc(piclen, 1);
	pic_out.size = piclen;
	if (pic_out.virAddr == 0) {
		fprintf(stderr, "calloc pic_out.virAddr failed\n");
		goto err_calloc_pic_out_virAddr;
	}

	if (fread(nal.p_payload, 1, opt->input_file_size, opt->input_file) != opt->input_file_size) {
		fprintf(stderr, "fread input file = %d failed\n", opt->input_file_size);
		goto err_fread_input_file;
	}

	if ((h = ijpegd_init(param)) == NULL) {
		fprintf(stderr, "ijpegd_init failed\n");
		goto err_ijpegd_init;
	}

	if (ijpegd_decode(h, &pic_out, &nal) < 0) {
		fprintf(stderr, "ijpegd_decode failed\n");
		goto err_ijpegd_decode;
	}

	/* write decoder data */
	if (fwrite((void *)pic_out.virAddr, 1, pic_out.size, opt->output_file) != pic_out.size) {
		fprintf(stderr, "fwrite pic_out.virAddr:%x bytes:%d failed:%s\n", pic_out.virAddr, pic_out.size, strerror(errno));
		goto err_fwrite_pic_out;
	}

	ijpegd_deinit(h);

	free((void *)pic_out.virAddr);
	pic_out.virAddr = 0;
	free(nal.p_payload);
	nal.p_payload = NULL;

	return 0;

err_fwrite_pic_out:
err_ijpegd_decode:
	ijpegd_deinit(h);
err_ijpegd_init:
err_fread_input_file:
	free((void *)pic_out.virAddr);
	pic_out.virAddr = 0;
err_calloc_pic_out_virAddr:
	free(nal.p_payload);
	nal.p_payload = NULL;
err_calloc_nal_p_payload:
	return -1;
}
